#!/usr/bin/env python
# -*- coding: utf-8 -*-

# graphicarea.py

# Copyright 2009 Diego Hansen Hahn (aka DiegoHH) <diegohh90 [at] hotmail [dot] com>

# lazynds is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License.

# lazynds is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with lazynds. If not, see <http://www.gnu.org/licenses/>.

#from __future__ import absolute_import

import warnings
import os
import tempfile

import gtk
from gtk import gdk
import cairo
import gobject

import timedebugger

from images import images

__author__ = "Diego Hansen Hahn"
__version__ = "v2.0.3"

LEFT_BUTTON = 1
RIGHT_BUTTON = 3


warnings.simplefilter('ignore', DeprecationWarning)

# Bloquear ou não bloquear IOError, eis a questão....
# Em raríssimas ocasiões, o desenho do buffer não consegue acompanhar a velocidade de mudança da imagem,
# por isso, é levantando IOError, para avisar que o arquivo temporário não exite. - Diego
warnings.simplefilter('ignore', IOError)

# Grid
xpm_data = ["64 64 3 1",
		    " 	c #FFFFFF",
			".	c #D8D8D8",
			"+  c #A8A8A8",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"...............................................................+",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"       .       .       .       .       .       .       .       +",
			"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"]

class GraphicWidget(gtk.DrawingArea):

	__gsignals__ = {'realize' : 'override',
			'expose-event' : 'override',
			'button-press-event' : 'override',
			'button-release-event' : 'override',
			'motion-notify-event' : 'override',
			'scroll-event' : 'override'}

	def __init__(self):
		gtk.DrawingArea.__init__(self)

		self.add_events(gdk.BUTTON_MOTION_MASK | gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK)

		self.dx = 0
		self.dy = 0

		self.over = None
		self.dragging = None

		self.set_size_request(0,0)
		self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))

	def add_container(self, container):
		self.container = container

#	@timedebugger.print_timing
	def do_expose_event(self, event):
		tile_pixmap, tile_mask = gtk.gdk.pixmap_create_from_xpm_d(
			self.window, None, xpm_data)
		tile_gc = self.window.new_gc(fill = gtk.gdk.TILED,
			tile = tile_pixmap)
		width = event.area.width
		height = event.area.height

		self.window.draw_rectangle(tile_gc, True, 0, 0, width, height)

		context = self.window.cairo_create()
		context.rectangle(event.area.x, event.area.y,
				  event.area.width, event.area.height)
		try:
			self.container.draw_context(context, self.dx, self.dy)
		except AttributeError:
			pass

		return False


	def do_realize(self):
		self.set_flags(self.flags() | gtk.REALIZED)

		events = gdk.EXPOSURE_MASK | gdk.BUTTON_PRESS_MASK | gdk.POINTER_MOTION_MASK

		self.window = gdk.Window(self.get_parent_window(),
						 x = self.allocation.x,
						 y = self.allocation.y,
						 width = self.allocation.width,
						 height = self.allocation.height,
						 window_type = gdk.WINDOW_CHILD,
						 wclass = gdk.INPUT_OUTPUT,
						 visual = self.get_visual(),
						 colormap = self.get_colormap(),
						 event_mask = self.get_events() | events)
		self.window.set_user_data(self)
		self.style.attach(self.window)
		self.style.set_background(self.window, gtk.STATE_NORMAL)

	def do_button_press_event(self, event):
		if event.button == LEFT_BUTTON:

			self.window.set_cursor(gdk.Cursor(gdk.FLEUR))

			if self.over:
				self.container.unlock()
				self.dragging = self.over.get_position()
				self.dx = event.x - self.dragging[0]
				self.dy = event.y - self.dragging[1]


		return False

	def do_button_release_event(self, event):
		if event.button == LEFT_BUTTON:

			self.window.set_cursor(gdk.Cursor(gdk.HAND2))

			if self.dragging:
				self.dragging = None
				self.container.set_position()
				self.container.lock()
				self.refresh()

	def do_motion_notify_event(self, event):
		pos = (event.x, event.y)

		if self.dragging:
			self.over.set_position(pos)
			self.refresh()
			return False

		if self.container.is_mouse_over(pos):
			self.window.set_cursor(gdk.Cursor(gdk.HAND2))
			self.over = self.container
		else:
			self.over = None
			self.window.set_cursor(None)

	def do_scroll_event(self, event):
		direction = event.direction

		ratio = self.container.get_scale()
		if direction == gdk.SCROLL_UP:
			if ratio < 8:
				ratio += ratio
				self.container.set_scale(ratio)
				self.container.draw_image()
				self.refresh()
		if direction == gdk.SCROLL_DOWN:
			if ratio > 1:
				ratio -= ratio/2
				self.container.set_scale(ratio)
				self.container.draw_image()
				self.refresh()
		return

	def refresh(self):
		alloc = self.get_allocation()
		rect = gdk.Rectangle(0, 0, alloc.width, alloc.height)
		self.window.invalidate_rect(rect, True)
		self.window.process_updates(True)
		return False

class ImageContainer(gobject.GObject):

	__gsignals__ = {'set-padding': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
								  (gobject.TYPE_PYOBJECT,))}

	@property
	def mode_1d(self):
		return 1
	@property
	def mode_2d(self):
		return 2

	def __init__(self, position):
		gobject.GObject.__init__(self)
		self.ratio = 1

		self.position = position
		self.x, self.y = self.position

		self.locker = True

		self.width = 0
		self.height = 0
		self.mode = self.mode_1d

		self.tempfile = None
		self.transparent = False

		self.shift_value = 0

		self.error_pixmap = None

	def __ne__(self, buffer):
		if hasattr(self, 'buffer'):
			return self.buffer.__ne__(buffer)
		else:
			return False

	def set_palette(self, palette):
		self.palette = palette

	def get_size(self):
		return (self.width, self.height)

	def set_buffer (self, buffer, width, height):
		if buffer:
			self.buffer = buffer

			self.width = width
			self.height = height

		else:
			self.buffer = False

	def is_mode(self, mode):
		if self.mode == mode:
			return True
		else:
			return False

	def get_mode(self):
		return self.mode

	def set_mode(self, mode):
		self.mode = mode

	def toggle_mode(self):
		if self.mode == self.mode_1d:
			self.set_mode(self.mode_2d)
		else:
			self.set_mode(self.mode_1d)

#	@timedebugger.print_timing
	def buffer2image(self):
		if hasattr(self, 'buffer'):
			if self.buffer:
				# Remove o temporário antigo, se existir
				if not isinstance(self.tempfile, type(None)):
					self.tempfile.close()
					os.unlink(self.tempfile.name)

				self.tempfile = tempfile.NamedTemporaryFile(delete=False)
				self.tempfile.seek(0,0)
				w = images.Writer((self.width, self.height), self.palette, self.palette.get_codec(), self.mode, True)
				if os.name == 'nt':
					padding = w.write(self.tempfile.file, self.buffer[self.shift_value:], 8, 'PNG')
				else:
					padding = w.write(self.tempfile, self.buffer[self.shift_value:], 8, 'PNG')
				self.tempfile.close()

				self.draw_image()
				self.emit('set-padding', padding)
			else:
				self.draw_image()

	def draw_image(self):
		if hasattr(self, 'buffer'):
			if self.buffer:
				self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, (self.width+9) * self.ratio, (self.height+9) * self.ratio)

				context = cairo.Context(self.surface)
				pixbuf = gtk.gdk.pixbuf_new_from_file(self.tempfile.name)
				context = gtk.gdk.CairoContext(context)

				context.set_source_pixbuf(pixbuf.scale_simple(pixbuf.get_width() * self.ratio, pixbuf.get_height() * self.ratio, gdk.INTERP_TILES), 7, 7)

				context.paint()

				context.set_line_width(2)
				context.set_source_rgb(1, 0, 0)
				context.rectangle(7, 7, pixbuf.get_width()* self.ratio + 1,pixbuf.get_height()* self.ratio + 1)
				context.stroke()

			else:
				self.surface =  cairo.ImageSurface(cairo.FORMAT_ARGB32, 40, 40)
				context = cairo.Context(self.surface)
				pixbuf = gtk.gdk.pixbuf_new_from_file(self.error_pixmap)
				context = gtk.gdk.CairoContext(context)
				context.set_source_pixbuf(pixbuf, 0, 0)

				context.paint()

	def draw_context(self, context, dx, dy):
		if self.locker == False:
			self.x, self.y = self.position
			self.x -= dx
			self.y -= dy
		self.x = self.x - self.x % 8
		self.y = self.y - self.y % 8
 		context.set_source_surface(self.surface, self.x, self.y)
		context.paint()

	def unlock(self):
		self.locker = False

	def lock(self):
		self.locker = True

	def set_scale(self, ratio):
		self.ratio = ratio

	def get_scale(self):
		return self.ratio

	def set_position(self, pos = None):
		if pos == None:
			self.position = (self.x, self.y)
		else:
			self.position = pos

	def get_position(self):
		return self.position

	def is_mouse_over (self, pos):
		if (pos[0] > self.position[0] and pos[0] < (self.position[0] + self.width * self.ratio )) and \
		   (pos[1] > self.position[1] and pos[1] < (self.position[1] + self.height * self.ratio )):
			return True
		return False
